﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MongoDB.Driver;
using BigData.DataContracts.Objects.BigData;
using MongoDB.Driver.Linq;
using MongoDB.Bson;

namespace BigDataFramework.Library.Helpers
{
    public static class MongoDBRepositoryHelper
    {
        private static object _registeredCollectionsMutex;
        private static Dictionary<string, string> _registeredCollections;
        private static DateTime _registeredCollectionsLastUpdate;

        private static bool _isRegisteredCollectionsDirty;

        private static object _addRegisteredCollectionsMutex;

        public const string CONNECTIONSTRING = "mongodb://localhost";
        public const string DATABASENAME = "BigDataDb";

        public static int RegisteredCollectionsCacheRefreshTimeInMinutes = 1440; // 1 day
        public static Dictionary<string, string> RegisteredCollections
        {
            get
            {
                // TODO: Implement better CACHING
                // If the registered collection has been set, or the time since last update is less than a day, update the object
                if (
                            _registeredCollections != null
                        &&  (DateTime.Now - _registeredCollectionsLastUpdate < new TimeSpan(0, RegisteredCollectionsCacheRefreshTimeInMinutes, 0))
                        &&  !_isRegisteredCollectionsDirty
                    )
                    return _registeredCollections;

                // Lock the object
                lock (_registeredCollectionsMutex)
                {
                    lock (_addRegisteredCollectionsMutex)
                    {
                        // If the registered collection has been set somewhere between the original check and the lock, then return that data
                        if (
                                    _registeredCollections != null
                                && (DateTime.Now - _registeredCollectionsLastUpdate < new TimeSpan(0, RegisteredCollectionsCacheRefreshTimeInMinutes, 0))
                                && !_isRegisteredCollectionsDirty
                            )
                            return _registeredCollections;

                        // Retrieve the registered collection object from the database (Note this is done this way because this property is usually called by the MongoDB repository
                        MongoCollection mc = MongoServer.Create(CONNECTIONSTRING).GetDatabase(DATABASENAME).GetCollection(ConvertTypeToCollectionName(typeof(RegisteredCollection)));

                        _registeredCollectionsLastUpdate = DateTime.Now;

                        // Parse and return the new registered collections object
                        return _registeredCollections = mc.FindAllAs<RegisteredCollection>().ToDictionary(k => k.ObjectTypeName, v => v.CollectionName);
                    }
                }
            }
        }

        public static void AddRegisteredCollection(RegisteredCollection rc)
        {
            // Retrieve the registered collection object from the database (Note this is done this way because this property is usually called by the MongoDB repository
            MongoCollection mc = MongoServer.Create(CONNECTIONSTRING).GetDatabase(DATABASENAME).GetCollection(ConvertTypeToCollectionName(typeof(RegisteredCollection)));

            if (mc.AsQueryable<RegisteredCollection>().Where(r => r.ObjectTypeName == rc.ObjectTypeName).ToList().Count > 0)
                return;

            lock (_addRegisteredCollectionsMutex)
            {
                if (mc.AsQueryable<RegisteredCollection>().Where(r => r.ObjectTypeName == rc.ObjectTypeName).ToList().Count > 0)
                    return;

                mc.Insert<RegisteredCollection>(rc);
                _isRegisteredCollectionsDirty = true;
            }
        }

        static MongoDBRepositoryHelper()
        {
            _registeredCollectionsMutex = new Object();
            _addRegisteredCollectionsMutex = new Object();
            _registeredCollectionsLastUpdate = DateTime.MinValue;
            _isRegisteredCollectionsDirty = false;
        }

        public static string ConvertTypeToCollectionName(Type type)
        {
            return type.FullName.Replace(".", "__dot__");
        }

        /// <summary>
        /// Parses the passed in ObjectId and throws an exception if it fails
        /// </summary>
        /// <param name="OID"></param>
        /// <returns></returns>
        public static ObjectId ConvertStringToObjectId(string OID)
        {
            ObjectId output;

            if (!ObjectId.TryParse(OID, out output))
                throw new Exception(string.Format("Unable to parse passed in oid {0}", OID));

            return output;
        }
    }
}
